﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{

    public class Cylinder3d : Solid3d
    {
        //public bool Rounded { get; set; }
        private double radiusTop;
        private double radiusBottom;
        private double height;
        private int radialSegments;
        private bool openEnded;
        private double thetaStart;
        private double thetaLength;
        private Vector3 normal= new Vector3(0, 0, 1);
        public double RadiusTop { get => radiusTop; set => SetSize (value, radiusBottom, height, thetaStart, thetaLength, radialSegments, openEnded, normal); }
        public double RadiusBottom { get => radiusBottom; set => SetSize(radiusTop, value, height, thetaStart, thetaLength, radialSegments, openEnded, normal); }
        public double Height { get => height; set => SetSize(radiusTop, radiusBottom, value, thetaStart, thetaLength, radialSegments, openEnded, normal); }
        public int RadialSegments { get => radialSegments; set => SetSize(radiusTop, radiusBottom, height, thetaStart, thetaLength, value, openEnded, normal); }
        public bool OpenEnded { get => openEnded; set => SetSize(radiusTop, radiusBottom, height, thetaStart, thetaLength, radialSegments, value, normal); }
        public double ThetaStart { get => thetaStart; set => SetSize(radiusTop, radiusBottom, height, value, thetaLength, radialSegments, openEnded, normal); }
        public double ThetaLength { get => thetaLength; set => SetSize(radiusTop, radiusBottom, height, thetaStart, value, radialSegments, openEnded, normal); }
        public Vector3 Normal { get => this.normal; set => SetSize(radiusTop, radiusBottom, height, thetaStart, thetaLength, radialSegments, openEnded, value); }

        public Cylinder3d(double radiusTop = 1, double radiusBottom = 1, double height = 1, double thetaStart = 0, double thetaLength = Math.PI * 2, int radialSegments = 32,  bool openEnded = false)
        {
            this.SetSize(radiusTop, radiusBottom, height, thetaStart, thetaLength, radialSegments,  openEnded,this.normal);
        }
        public Surface3d[] Surfaces;
        private TopoFaceModel topoFaceModel;
        public Cylinder3d SetSize(double radiusTop , double radiusBottom , double height, double thetaStart, double thetaLength , int radialSegments,  bool openEnded,Vector3 normal)
        {
            if (topoFaceModel != null && (this.radiusTop != radiusTop || this.radiusBottom != radiusBottom || 
                this.height != height || this.radialSegments != radialSegments ||  this.openEnded != openEnded||
                 this.thetaStart != thetaStart || this.thetaLength != thetaLength || this.normal != normal))
            {
                topoFaceModel = null;
            }
            this.radiusTop = radiusTop ;
            this.radiusBottom = radiusBottom ;
            this.height = height ; 
            this.radialSegments = radialSegments ; 
            this.openEnded = openEnded ;
            this.thetaStart = thetaStart ;
            this.thetaLength = thetaLength;
            this.normal = normal ;
            return this;
        }
        private List<Vector3> btmVertexes = new List<Vector3>(); //圆柱体/圆锥台 底部轮廓点
        private List<Vector3> topVertexes = new List<Vector3>(); //圆柱体/圆锥台 顶部轮廓点
        public override TopoFaceModel CreateTopoModel()
        {
            if (topoFaceModel != null)
                return topoFaceModel;
            var cylindricalSurface1 = new CylindricalSurface3d(radiusTop, radiusBottom, height, ThetaStart, ThetaStart + ThetaLength / 2, new Vector3(), Normal, this.radialSegments / 2);
            var cylindricalSurface2 = new CylindricalSurface3d(radiusTop, radiusBottom, height, ThetaStart + ThetaLength / 2, ThetaStart + ThetaLength, new Vector3(), Normal, this.radialSegments / 2);
            var surfaces = new List<Surface3d>()
            {
                cylindricalSurface1,
                cylindricalSurface2,
            };
            if (!openEnded)
            {
                var origin = new Vector3();
                var topOrigin = origin.Clone().AddScaledVector(Normal.Normalize(), height);
                var topCurve1 = new Arc3d(topOrigin, RadiusTop, ThetaStart, ThetaStart + ThetaLength/2);
                topCurve1.UseFixedDiv(radialSegments);
                var topCurve2 = new Arc3d(topOrigin, RadiusTop, ThetaStart + ThetaLength / 2, ThetaStart + ThetaLength);
                topCurve2.UseFixedDiv(radialSegments);
                topVertexes = topCurve1.GetPoints();
                topVertexes.AddRange(topCurve2.GetPoints());

                var topFace = new PlanarSurface3d(new Plane(new Vector3(0, 0, 1)), new List<Curve3d> { topCurve1, topCurve2 });
                surfaces.Add(topFace);

                var btmCurve1 = new Arc3d(origin, RadiusBottom, ThetaStart, ThetaStart + ThetaLength / 2);
                btmCurve1.UseFixedDiv(radialSegments);
                var btmCurve2 = new Arc3d(origin, RadiusBottom, ThetaStart + ThetaLength / 2, ThetaStart + ThetaLength);
                btmCurve2.UseFixedDiv(radialSegments);
                btmVertexes = btmCurve1.GetPoints();
                btmVertexes.AddRange(btmCurve2.GetPoints());

                var btmFace = new PlanarSurface3d(new Plane(new Vector3(0, 0, -1)), new List<Curve3d> { btmCurve1, btmCurve2 });
                surfaces.Add(btmFace);
            }

            this.Surfaces = surfaces.ToArray();
            this.topoFaceModel = new TopoFaceModel() { Surfaces = this.Surfaces.ToListEx() };
            return this.topoFaceModel;
        }
        public override Solid3d Mesh()
        {
            base.Mesh();

            //var idxsCount = this.Geometry.Indics.Length;
            //var aroundIdxCount = radialSegments * 2 * 3; 
            //var btmIdxCount = (idxsCount - aroundIdxCount)/2;
            //this.Geometry.Groups = new GeometryGroup[4]
            //{
            //    new GeometryGroup{ Name = "AroundL", Start = 0, Count = aroundIdxCount/2 , MaterialIndex = 0 },
            //    new GeometryGroup{ Name = "AroundR", Start = aroundIdxCount/2, Count = aroundIdxCount/2 , MaterialIndex = 0 },
            //    new GeometryGroup{ Name = "Top", Start = aroundIdxCount, Count = btmIdxCount, MaterialIndex = 1},
            //    new GeometryGroup{ Name = "Bottom", Start = aroundIdxCount + btmIdxCount, Count = btmIdxCount, MaterialIndex = 2}
            //};
            if (!openEnded)
            {
                this.Geometry.Groups[2].MaterialIndex = 1;
                this.Geometry.Groups[3].MaterialIndex = 2;
            }
            this.Edge = new GeometryData();
            var verteics = new List<Vector3>();
            var idxArray = new ListEx<int>() { };
            for (var i = 0; i < (radialSegments+1)*2; i++)
            {
                var count = verteics.Count;
                verteics.Add(topVertexes[i]);
                verteics.Add(btmVertexes[i]);
                idxArray.Push(count, count + 1);
                idxArray.Push(count, count + 2);
                idxArray.Push(count + 1, count + 3);
            }
            verteics.Add(topVertexes[radialSegments * 2 + 1]);
            verteics.Add(btmVertexes[radialSegments * 2 + 1]);
            this.Edge.Verteics = Utils.GenerateVertexArr(verteics);
            this.Edge.Indics = idxArray.ToArray(); 
            return this;
        }
    }
}
